/**
 * 
 */
package tk.sunlatus.tools.secret;

import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import sun.security.jca.ProviderList;
import sun.security.jca.Providers;

/**
 * 加密、解密工具 <br>
 * 支持MD5，SHA-256和
 * 
 * @author Sunlotus
 * 
 */
public class SunlatusEncoder {

    private static final Charset CHARSET = Charset.forName("UTF-8");
    private static final String ALGORITHM_PBE_WITH_IDEA = "PBEWithMD5AndDES";
    private static final int encryptTimes = 100;
    private static final String DEFAULT_SALT = "www.sunlatus.tk";

    public String messageDigest(String data, String algorithm) {
        String result = "";
        try {
            MessageDigest digest = MessageDigest.getInstance(algorithm);
            byte[] resultBytes = digest.digest(data.getBytes(Charset.forName("UTF-8")));
            result = new String(Hex.encode(resultBytes), Charset.forName("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 方法描述：根据密码生成密钥
     * 
     * @param password
     *            密码
     * @return key 密钥
     */
    private Key toKey(String password) {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        Key key = null;
        try {
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_PBE_WITH_IDEA);
            key = keyFactory.generateSecret(keySpec);
        } catch (NoSuchAlgorithmException e) {
            // TODO log
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            // TODO log
            e.printStackTrace();
        }
        return key;
    }

    /**
     * 方法描述：添加加密算法提供者
     * 
     * @param provider
     */
    private void addProvider(Provider provider) {

        ProviderList list = Providers.getFullProviderList();
        if (list.getIndex(provider.getName()) > -1) {
            return;
        }
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 加密
     */
    public String encrypt(String data, String password, String salt) {

        if (salt == null) {
            salt = DEFAULT_SALT;
        }
        PBEParameterSpec paramSpec = new PBEParameterSpec(getByte(salt), encryptTimes);
        String encodedData = "加密失败";
        try {
            addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(ALGORITHM_PBE_WITH_IDEA);
            Key key = toKey(password);
            if (key == null) {
                // TODO logger.log("get key faild.");
                return encodedData;
            }
            cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            byte[] encodeBytes = cipher.doFinal(data.getBytes(CHARSET));
//            encodedData = new String(Hex.encode(encodeBytes), CHARSET);
            encodedData = Base64.encodeBase64String(encodeBytes);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encodedData;
    }

    private byte[] getByte(String salt) {
        byte[] result = new byte[8];

        if (salt.getBytes(Charset.forName("UTF-8")).length > result.length) {
            System.arraycopy(salt.getBytes(Charset.forName("UTF-8")), 0, result, 0, result.length);
        } else {
            result = salt.getBytes(Charset.forName("UTF-8"));
        }
        return result;
    }

    /**
     * 解密
     */
    public String decrypt(String data, String password, String salt) {

        if (salt == null) {
            salt = DEFAULT_SALT;
        }
        byte[] saltByte = getByte(salt);
        PBEParameterSpec paramSpec = new PBEParameterSpec(saltByte, encryptTimes);
        String encodedData = "解密失败";
        try {
            addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(ALGORITHM_PBE_WITH_IDEA);
            Key key = toKey(password);
            if (key == null) {
                // TODO logger.log("get key faild.");
                return encodedData;
            }
            cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//            byte[] encodeBytes = cipher.doFinal(Hex.decode(data.getBytes(CHARSET)));
            byte[] encodeBytes = cipher.doFinal(Base64.decodeBase64(data));
            encodedData = new String(encodeBytes, CHARSET);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encodedData;
    }
}
